package cn.spream.jstudy.httpclient;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultHttpClient;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: cn.spream
 * Date: 12-9-19
 * Time: 上午11:32
 * To change this template use File | Settings | File Templates.
 */
public class HttpClientUtils {

    //请求返回的初始结果
    private static final String INIT_RESULT = null;
    //ssl端口
    private static final int SCHEME_PORT = 443;

    /**
     * get请求
     * @param uri
     * @return
     */
    public static String get(final String uri) throws IOException, URISyntaxException {
        return get(new URI(uri));
    }

    /**
     * https的get请求
     * @param uri
     * @return
     * @throws URISyntaxException
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static String getOfSSL(final String uri) throws URISyntaxException, IOException, NoSuchAlgorithmException, KeyManagementException {
        return getOfSSL(new URI(uri));
    }

    /**
     * 根据地址获得一个文件
     * @param uri
     * @param path
     * @return
     * @throws IOException
     */
    public static File getForFile(final String uri, final String path) throws IOException, URISyntaxException {
        return getForFile(new URI(uri), path);
    }

    /**
     * 根据地址获得一个文件
     * @param uri
     * @param path
     * @return
     * @throws IOException
     */
    public static File getOfSSLForFile(final String uri, final String path) throws IOException, NoSuchAlgorithmException, KeyManagementException, URISyntaxException {
        return getOfSSLForFile(new URI(uri), path);
    }

    /**
     * get请求
     * @param uri
     * @return
     * @throws IOException
     */
    public static String get(final URI uri) throws IOException {
        isNotNull(uri);
        HttpClient httpClient = createBaseHttpClient();
        HttpGet httpGet = new HttpGet(uri);
        String result = requestOfString(httpClient, httpGet);
        return result;
    }

    /**
     * https的get请求
     * @param uri
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws IOException
     */
    public static String getOfSSL(final URI uri) throws NoSuchAlgorithmException, KeyManagementException, IOException {
        isNotNull(uri);
        HttpClient httpClient = createBaseHttpClientOfSSL();
        HttpGet httpGet = new HttpGet(uri);
        return requestOfString(httpClient, httpGet);
    }

    /**
     * 根据地址获得一个文件
     * @param uri
     * @param path
     * @return
     * @throws IOException
     */
    public static File getForFile(final URI uri, final String path) throws IOException {
        isNotNull(uri);
        isNotEmpty(path);
        HttpClient httpClient = createBaseHttpClient();
        HttpGet httpGet = new HttpGet(uri);
        return requestOfFile(httpClient, httpGet, path);
    }

    /**
     * 根据地址获得一个文件
     * @param uri
     * @param path
     * @return
     * @throws IOException
     */
    public static File getOfSSLForFile(final URI uri, final String path) throws IOException, NoSuchAlgorithmException, KeyManagementException {
        isNotNull(uri);
        isNotEmpty(path);
        HttpClient httpClient = createBaseHttpClientOfSSL();
        HttpGet httpGet = new HttpGet(uri);
        return requestOfFile(httpClient, httpGet, path);
    }

    /**
     * post请求
     * @param uri
     * @return
     * @throws IOException
     */
    public static String post(final String uri) throws IOException, URISyntaxException {
        return post(uri, (Map<String, String>)Collections.EMPTY_MAP);
    }

    /**
     * https的post请求
     * @param uri
     * @return
     * @throws IOException
     */
    public static String postOfSSL(final String uri) throws IOException, URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
        return postOfSSL(uri, (Map<String, String>) Collections.EMPTY_MAP);
    }

    /**
     * post请求
     * @param uri
     * @param params
     * @return
     */
    public static String post(final String uri, final Map<String, String> params) throws IOException, URISyntaxException {
        return post(new URI(uri), params);
    }

    /**
     * https的post请求
     * @param uri
     * @param params
     * @return
     */
    public static String postOfSSL(final String uri, final Map<String, String> params) throws IOException, URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
        return postOfSSL(new URI(uri), params);
    }

    /**
     * post请求
     * @param uri
     * @param params
     * @return
     */
    public static String post(final URI uri, final Map<String, String> params) throws IOException {
        isNotNull(uri,params);
        HttpClient httpClient = createBaseHttpClient();
        HttpPost httpPost = new HttpPost(uri);
        setParameter(httpPost, params);
        return requestOfString(httpClient, httpPost);
    }

    /**
     * https的post请求
     * @param uri
     * @param params
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws IOException
     */
    public static String postOfSSL(final URI uri, final Map<String, String> params) throws NoSuchAlgorithmException, KeyManagementException, IOException {
        isNotNull(uri);
        HttpClient httpClient = createBaseHttpClientOfSSL();
        HttpPost httpPost = new HttpPost(uri);
        setParameter(httpPost, params);
        return requestOfString(httpClient, httpPost);
    }

    /**
     * 获得SSL的HttpClient对象
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static HttpClient createBaseHttpClientOfSSL() throws NoSuchAlgorithmException, KeyManagementException {
        HttpClient httpClient = createBaseHttpClient();
        return createHttpClientOfSSL(httpClient);
    }

    /**
     * 获得SSL的HttpClient对象
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static HttpClient createHttpClientOfSSL(HttpClient httpClient) throws NoSuchAlgorithmException, KeyManagementException {
        SchemeRegistry schemeRegistry = httpClient.getConnectionManager().getSchemeRegistry();
        TrustManager trustManager = createX509TrustManager();
        SSLSocketFactory sslSocketFactory = createSSLSocketFactory(trustManager);
        schemeRegistry.register(new Scheme("https", SCHEME_PORT, sslSocketFactory));
        return httpClient;
    }

    /**
     * 设置post的参数
     * @param httpPost
     * @param params
     * @return
     */
    private static HttpPost setParameter(final HttpPost httpPost, final Map<String, String> params){
        for(Map.Entry<String, String> param : params.entrySet()){
            httpPost.getParams().setParameter(param.getKey(), param.getValue());
        }
        return httpPost;
    }

    /**
     * 执行request请求
     * @param httpClient
     * @param httpUriRequest
     * @return string
     * @throws IOException
     */
    private static String requestOfString(final HttpClient httpClient, final HttpUriRequest httpUriRequest) throws IOException {
        String result = INIT_RESULT;
        InputStream inputStream = null;
        try {
            HttpResponse httpResponse = httpClient.execute(httpUriRequest);
            if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                HttpEntity httpEntity = httpResponse.getEntity();
                Charset charset = fetchCharset(httpEntity);
                inputStream = httpEntity.getContent();
                result = IOUtils.toString(inputStream, charset);
            }
        } finally {
            IOUtils.closeQuietly(inputStream);
            httpClient.getConnectionManager().shutdown();
        }
        return result;
    }

    /**
     * 执行request请求
     * @param httpClient
     * @param httpUriRequest
     * @param path
     * @return 文件
     * @throws IOException
     */
    private static File requestOfFile(final HttpClient httpClient, final HttpUriRequest httpUriRequest, final String path) throws IOException {
        File file = new File(path);
        InputStream inputStream = null;
        try {
            HttpResponse httpResponse = httpClient.execute(httpUriRequest);
            if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                inputStream = httpResponse.getEntity().getContent();
                FileUtils.copyInputStreamToFile(inputStream, file);
            }
        } finally {
            IOUtils.closeQuietly(inputStream);
            httpClient.getConnectionManager().shutdown();
        }
        return file;
    }

    /**
     * 提取字符编码名称
     * @param httpEntity
     * @return
     */
    private static Charset fetchCharset(final HttpEntity httpEntity){
        Charset charset = null;
        if(httpEntity != null){
            ContentType contentType = ContentType.get(httpEntity);
            if(contentType != null){
                charset = contentType.getCharset();
            }
        }
        return charset;
    }

    /**
     * 检查参数是否为null
     * @param objects
     */
    private static void isNotNull(final Object ... objects){
        for(Object object : objects){
            if(object == null){
                throw new RuntimeException("非法的参数类型，参数不能为null.");
            }
        }
    }

    /**
     * 检查字符串是否为null或空字符
     * @param strings
     */
    private static void isNotEmpty(final String ... strings){
        for(String string : strings){
            if(StringUtils.isBlank(string)){
                throw new RuntimeException("非法的参数类型，参数不能为null或空字符");
            }
        }
    }

    /**
     * 我们将需要告诉client使用一个不同的TrustManager。
     * TrustManager是一个检查给定的证书是否有效的类。
     * SSL使用的模式是X.509，对于该模式Java有一个特定的TrustManager，称为X509TrustManager。
     * 首先我们需要创建这样的TrustManager。
     * 如果一个证书是无效的，那么TrustManager在checkXXX方法中将抛出CertificateException异常。
     * 既然我们总是接受所有的证书，我们从来不抛出异常。
     * @return
     */
    private static X509TrustManager createX509TrustManager(){
        X509TrustManager x509TrustManager = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
            public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
            public X509Certificate[] getAcceptedIssuers() {return null;}
        };
        return x509TrustManager;
    }

    /**
     * SSLSocketFactory
     * @param trustManager
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    private static SSLSocketFactory createSSLSocketFactory(TrustManager trustManager) throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{trustManager}, null);
        return new SSLSocketFactory(sslContext);
    }

    /**
     * 获得HttpClient对象
     * @return
     */
    private static HttpClient createBaseHttpClient(){
        HttpClient httpClient = new DefaultHttpClient();
        return httpClient;
    }

}
